// hpp 表示header only 开源代码
// 将函数定义和声明放在一起

#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>
#include <cassert>

// 先声明 要使用
class Thread;

// 上下文，当大号结构体使用
// 用Context来处理静态方法调用不了成员变量的问题
class Context
{
public:
    Thread *this_;
    void *args_;

public:
    Context() : this_(nullptr), args_(nullptr)
    {}
    ~Context()
    {}
};

// 对线程做封装，以后就不需要总去调用原生库的接口了
class Thread
{
public:
    // using func_t = std::function<void*(void*)>; -- C++11 智能指针
    typedef std::function<void *(void *)> func_t;
    const int num = 1024;

    // "void *(Thread::*)(void *args)" 类型的实参与 "void *(*)(void *)" 类型的形参不兼容C/C++(167) -- 报错
    // 为啥呢？ -- 因为start_routine是类内的成员函数，有缺省参数this指针
    // void* start_routine(void* args)
    // {
    //     return func_(args);
    // }

    // 在类内创建线程  这样就没有this指针了
    static void *start_routine(void *args)
    {
        // 但是没有this指针
        // 静态方法不能调用成员方法或成员变量
        // return func_(args);
        // 可以采用友元，或者public成员的方式 这里不用！！
        
        // 将args强转成Context的指针
        Context *ctx = static_cast<Context*>(args);
        // 调用run方法 -- 将方法从静态中剥离出来
        void* ret = ctx->this_->run(ctx->args_);
        delete ctx;
        return ret;
    }

    // 构造函数
    Thread(func_t func, void *args = nullptr, int number = 0) : func_(func), args_(args)
    {
        char buffer[num];
        snprintf(buffer, sizeof buffer, "thread-%d", number);
        name_ = buffer;

        // 加上Context
        Context* ctx = new Context();
        ctx->this_ = this;
        ctx->args_ = args_;
        // 将ctx传过去
        int n = pthread_create(&tid_, nullptr, start_routine, ctx);
        //int n = pthread_create(&tid_, nullptr, start_routine, args);
        assert(n == 0);
        (void)n;
    }

    // 线程等待
    void join()
    {
        int n = pthread_join(tid_, nullptr);
        assert(n == 0);
        (void)n;
    }

    void *run(void *args)
    {
        return func_(args);
    }

    // 析构
    ~Thread()
    {
        // do nothing
    }

private:
    // 自定义线程名 方便观察(不需要通过观察tid)
    // 末尾加_ 将成员变量和参数做区分
    std::string name_;
    pthread_t tid_;
    // 线程执行任务
    func_t func_;
    // 获取参数
    void *args_;
};
